package com.linkin.homepage.view;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.animation.Interpolator;

import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

import com.linkin.homepage.base.util.ReflectUtils;

public class FixedRecyclerView extends RecyclerView {

    public interface OnDrawListener {
        void onDraw();
    }

    private int mScrollPointerId = -1;
    private int mLastTouchX;
    private int mLastTouchY;
    private OnDrawListener mDrawListener = null;

    private boolean mHookVelocity = false;
    private int mVelocityY = 0;
    private int mMaxFlingVelocity = Integer.MAX_VALUE;
    private VelocityTracker mVelocityTracker;

    public FixedRecyclerView(Context context) {
        super(context);
        init(context, null);
    }

    public FixedRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public FixedRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        closeDefaultAnimator();
    }

    private void closeDefaultAnimator() {
        this.getItemAnimator().setAddDuration(0);
        this.getItemAnimator().setChangeDuration(0);
        this.getItemAnimator().setMoveDuration(0);
        this.getItemAnimator().setRemoveDuration(0);
    }

    public void setInterpolator(Interpolator interpolator) {
        Object viewFlinger = ReflectUtils.getFieldValue(this, RecyclerView.class, "mViewFlinger");
        if (viewFlinger != null) {
            ReflectUtils.setField(viewFlinger, viewFlinger.getClass(), "mInterpolator", interpolator);

            Object scroller = ReflectUtils.getFieldValue(viewFlinger, viewFlinger.getClass(), "mScroller");
            if (scroller != null) {
                ReflectUtils.setField(scroller, scroller.getClass(), "mInterpolator", interpolator);
            }
        }
    }

    public void setOnDrawListener(OnDrawListener drawListener) {
        mDrawListener = drawListener;
        setWillNotDraw(false);
    }

    public void setHookVelocityBehavior(boolean hookVelocity) {
        mHookVelocity = hookVelocity;
        if (mHookVelocity) {
            final ViewConfiguration vc = ViewConfiguration.get(getContext());
            mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
        }
    }

    @Override
    public void draw(Canvas canvas)  {
        super.draw(canvas);
        if (mDrawListener != null) {
            mDrawListener.onDraw();
        }
    }

    @Override
    public boolean fling(int velocityX, int velocityY) {
        if (mHookVelocity) {
            velocityY = mVelocityY;
        }
        return super.fling(velocityX, velocityY);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        return onInterceptTouchEvent(e, true);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        return onInterceptTouchEvent(e, false);
    }

    private boolean onInterceptTouchEvent(MotionEvent e, boolean interceptTouchEvent) {
        if (isLayoutFrozen()) {
            return false;
        }

        if (getLayoutManager() == null) {
            return false;
        }

        final boolean canScrollHorizontally = getLayoutManager().canScrollHorizontally();
        final boolean canScrollVertically = getLayoutManager().canScrollVertically();

        final int action = e.getActionMasked();
        final int actionIndex = e.getActionIndex();

        if (mHookVelocity) {
            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            if (action == MotionEvent.ACTION_DOWN) {
                mVelocityTracker.clear();
                mVelocityY = 0;
            }

            final MotionEvent vtev = MotionEvent.obtain(e);
            vtev.setLocation(vtev.getX(), e.getRawY());
            mVelocityTracker.addMovement(vtev);

            if (action == MotionEvent.ACTION_UP) {
                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
                mVelocityY = (int) (canScrollVertically ? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0);
            }
        }

        if (action == MotionEvent.ACTION_DOWN) {
            mScrollPointerId = e.getPointerId(0);
            mLastTouchX = (int) (e.getX() + 0.5f);
            mLastTouchY = (int) (e.getY() + 0.5f);
            return onInterceptTouchEventPrivate(e, interceptTouchEvent);
        }

        if (action == MotionEvent.ACTION_POINTER_DOWN) {
            mScrollPointerId = e.getPointerId(actionIndex);
            mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
            mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
            return onInterceptTouchEventPrivate(e, interceptTouchEvent);
        }

        if (action == MotionEvent.ACTION_MOVE) {
            final int index = e.findPointerIndex(mScrollPointerId);
            if (index < 0) {
                return onInterceptTouchEventPrivate(e, interceptTouchEvent);
            }

            final int x = (int) (e.getX(index) + 0.5f);
            final int y = (int) (e.getY(index) + 0.5f);
            if (getScrollState() != SCROLL_STATE_DRAGGING) {
                final int dx = mLastTouchX - x;
                final int dy = mLastTouchY - y;

                boolean startScroll = false;
                if (canScrollHorizontally && Math.abs(dx) > Math.abs(dy)) {
                    startScroll = true;
                }
                if (canScrollVertically && Math.abs(dy) > Math.abs(dx)) {
                    startScroll = true;
                }

                if (startScroll && onInterceptTouchEventPrivate(e, interceptTouchEvent)) {
                    mLastTouchX = x;
                    mLastTouchY = y;
                    return true;
                }
                return false;
            }
            return onInterceptTouchEventPrivate(e, interceptTouchEvent);
        }

        if (action == MotionEvent.ACTION_POINTER_UP) {
            if (e.getPointerId(actionIndex) == mScrollPointerId) {
                // Pick a new pointer to pick up the slack.
                final int newIndex = actionIndex == 0 ? 1 : 0;
                mScrollPointerId = e.getPointerId(newIndex);
                mLastTouchX = (int) (e.getX(newIndex) + 0.5f);
                mLastTouchY = (int) (e.getY(newIndex) + 0.5f);
            }
        }

        return onInterceptTouchEventPrivate(e, interceptTouchEvent);
    }

    private boolean onInterceptTouchEventPrivate(MotionEvent ev, boolean interceptTouchEvent) {
        if (interceptTouchEvent) {
            return super.onInterceptTouchEvent(ev);
        }
        return super.onTouchEvent(ev);
    }
}